# -*- coding:utf-8 -*-
import time

import datetime
from bigchaindb import Bigchain
from bigchaindb.common.transaction import TransactionLink, Output, Input
from bigchaindb.models import Transaction
from blockchainrestful.money import Money

class Bill(Money):
    # BLACKHOLE_PUB_KEY = "EtEsjpRedzezXsej1PsPBBCtHDL5LjBYXcJDQWHbhuFHCwNKSaWgKCyWoDy8jqTxNabt4QV5mrrpYt7qX9oZeHHGHonyHeozFnzoA"
    # BANK_PRIV_KEY = "CUZABR4aiN4VSCm52FLBzW2mw9XYYWoEw5wTygHQ6S9i5pVsWvJrrMsVtp5LuPRiizh28cG6G4UecQZgo7Y2Az9hk4siSGiRQA3rsv9V9PEFwWMTJNSZS8TshAhZQDfyQDupwVnFBvqyEgb1tctRkewFQbUBesV1A2oHAFDdqYocdSD2ji5mnLc3Ce8YmqayrDGz7YZS5rAaMk9CFhx1tCEytXJkLbEYorheUe9tC9wrfuJjqb2zZWUPpUFa9gYmVG5VhQgixqE4DgCpfAiTnUcXmcdWFzpCipbL67nBSEWzBBV4kaX1VDYYhCZeiBUuMZ31TBLWWmRaKomqt82zDWFpkds8DSjhbA7neufcw5UCF12xY62icLCZgU99dRqPLAsdecujWhqrLxSx4JtHwcwCruLxssKoU7LGwU7S5SQHVNt4zcKZH"
    # BANK_PUB_KEY = "EtEsjpSAK7Zc1i782Bvo8r8F7aeDRdfzAHAZbn9AAzKCv59mqWtBkrvEfP3i9hGgjTvzzsSZG3FqHty6Rh5gAMgvWQ7L9BV818tQg"
    # SUPER_PRIV_KEY = "4rZ3djVfXiXxejdbVKygt5jVHnzdM4ZqB77rJYDuueLzST5xYqUL33M21Zcqf9tgzj9ZBvDDzeTq4bveeg4v8W1efVEk3KYYz3RqbvwJAxsLLwCiohxiaoWkoEFKwJTLETgwNgD3aCU7b3ehnAMXj4Er2v94zDkx2CMNzj2UxWKpj2tgUU5Lfm6VkiCpFedZmtbyfMRXe2PRvwHwNSwQ2YzeKdk4KQmUTQEf5n6LTKRmp1A8PfJjbMgDmkxA72DN2P6v5cPWMs3p2Xe26vRyBFyMiGXLPjhst8mdfASyYW4qCYBwGUPnz6c2PLZBNLCbwuMK4fbPz45eWhxHWpqfgzo3Vd3qU1zwni6nv4ceVim8rGomxNfLpBcyBGTZ9r5Qfa69uw5mJQZVRYQpxQexYMnWcf4bnKN5x5fqUF7cq5kHjGpGfUC5gXHC"
    # SUPER_PUB_KEY = "EtEsjpSdV2dx28EuXUvrvCLXXtva4YAJ1dY2RZx71adBb7T87p3qDhrm6NaHekPQUZPcTPtNwxXGm4awWCU4Mmvfy3MGHxRfdtcMN"
    # ASSET_AGGRE_THRESHOLD = 10
    # ASSET_TYPE = {'money': 'money RMB', 'bill': 'bill'}
    # TRANSAC_TYPE = {'aggre': 'asset aggregation'}
    # TIMEOUT = 30

    def __init__(self):
        self.b = Bigchain()
        self.asset = {'asset': self.ASSET_TYPE['bill']}
        pass

    def filter_by_bill_asset(self, txs, indexs):
        filtered_txs = []
        filtered_indexs = []
        for i in range(len(txs)):
            if 'asset' in txs[i]["metadata"] and txs[i]["metadata"]['asset'] == self.ASSET_TYPE['bill']:
                if not self.b.is_tx_strictly_in_invalid_block(txs[i]['id']):
                    filtered_txs.append(txs[i])
                    filtered_indexs.append(indexs[i])

        return (filtered_txs, filtered_indexs)

    def get_unspent_outputs(self, owner):
        output_txs, output_indexs = self.b.get_tx_outputs(owner)
        input_txs, input_indexs = self.b.get_tx_inputs(owner)
        output_txs, output_indexs = self.filter_by_bill_asset(output_txs, output_indexs)
        input_txs, input_indexs = self.filter_by_bill_asset(input_txs, input_indexs)
        txs = []
        indexs = []
        for i in range(len(output_txs)):
            spent = False
            for j in range(len(input_txs)):
                input = input_txs[j]["inputs"][input_indexs[j]]
                if input["fulfills"]["txid"] == output_txs[i]["id"] and input["fulfills"]["output"] == output_indexs[i]:
                    spent = True
                    break
            if not spent:
                txs.append(output_txs[i])
                indexs.append(output_indexs[i])
        return (txs, indexs)

    # def is_send(self, txid, idx, owner):
    #     spent = False
    #     # tx = self.b.get_transaction(txid=txid)
    #     # output = tx.outputs[idx]
    #     input_txs, input_indexs = self.b.get_tx_inputs(owner)
    #     for j in range(len(input_txs)):
    #         input = input_txs[j]['inputs'][0]
    #         if self.candr(input["fulfills"],"txid") == txid:
    #             if not self.b.is_tx_strictly_in_invalid_block(input_txs[j]['id']):
    #                 spent = True
    #
    #     return spent

    def go_through_all_bills(self, account):
        txs, indexs = self.get_unspent_outputs(account)
        # translinks = self.b.get_owned_ids(account)
        total = 0
        account_sets = []
        for i in range(len(txs)):
            # for translink in translinks:
            tx = Transaction.from_dict(txs[i])
            index = indexs[i]
            asset_id = tx.asset['id'] if 'id' in tx.asset else tx.id

            total = total + tx.outputs[index].amount
            account_sets.append({"translink": TransactionLink(tx.id, index),
                                 "tx": tx,
                                 "asset_id": asset_id,
                                 "amount": tx.outputs[index].amount})
        return (total, account_sets)

        # modify the owner to the first one, change the weight and threshold

    def conv_output(self, orig_output):
        orig_output.public_keys = [orig_output.public_keys[0]]
        output = orig_output.to_dict()
        output['condition']['details']['threshold'] = len(output['condition']['details']['subfulfillments']) - 1
        for j in range(len(output['condition']['details']['subfulfillments'])):
            if output['condition']['details']['subfulfillments'][j]['public_key'] == orig_output.public_keys[0]:
                output['condition']['details']['subfulfillments'][j]['weight'] \
                    = output['condition']['details']['threshold']
        return Output.from_dict(output)

        # wait until all the tx are successful or timeout

    def wait_for_tx_result(self, tx_ids, timeout):
        print('-wait_for_tx_result---tx_ids----', tx_ids)
        count = 0
        while True:
            interval = 0.2
            time.sleep(interval)
            ret = True
            err_txids = []
            succ_txids = []
            cout = 0
            for txid in tx_ids:
                temp, status = self.b.get_transaction(txid, include_status=True)
                # if status != self.b.TX_VALID:
                #     ret = False
                #     err_txids.append(txid)
                # elif status == self.b.TX_VALID:
                #     succ_txids.append(txid)

                # print('---------status---------',status)

                if status == self.b.BLOCK_INVALID:
                    cout = cout + 1
                    ret = False
                    err_txids.append(txid)
                elif status == self.b.TX_VALID:
                    cout = cout + 1
                    succ_txids.append(txid)
                else:
                    ret = False
                    err_txids.append(txid)
            if ret:
                return {"ret": True, "succ_txids": succ_txids}
            count = count + interval
            if count >= timeout or cout == len(tx_ids):
                print('-wait_for_tx_result---err_txids----', err_txids)
                for txid in err_txids:
                    self.b.delete_transaction(txid)
                return {"ret": False, "err": "failed"}

    def get_bill_list(self, account):
        total, account_sets = self.go_through_all_bills(account)
        amounts = []
        # txids = []
        for account_set in account_sets:
            amounts.append(account_set["amount"])
            # txids.append(account_set['tx'].id)
        print(amounts)
        # print(txids)

        return account_sets

    def deposit_bill(self, account, amount, metadata=None, timeout=Money.TIMEOUT):
        meta = {'group': time.time(), 'asset': self.ASSET_TYPE['bill']}
        if metadata != None:
            meta.update(metadata)
        tx = Transaction.create([self.BANK_PUB_KEY_NEW], [([account, self.BANK_PUB_KEY_NEW, self.SUPER_PUB_KEY_NEW], amount)],
                                asset=self.asset, metadata=meta)

        for i in range(len(tx.outputs)):
            tx.outputs[i] = self.conv_output(tx.outputs[i])

        tx = tx.sign([self.BANK_PRIV_KEY_NEW])
        self.b.write_transaction(tx)

        return self.wait_for_tx_result([tx.id], timeout)

    def transfer_single_transaction(self, account_from, account_to, txid, idx, amount, metadata=None,
                                    keyfree=True):
        print('---transfer_single_transaction---keyfree----', keyfree)
        tx = self.b.get_transaction(txid=txid)

        superpubkey = self.SUPER_PUB_KEY_NEW
        superprikey = self.SUPER_PRIV_KEY_NEW

        bankpubkey = self.BANK_PUB_KEY_NEW
        bankprikey = self.BANK_PRIV_KEY_NEW

        subfulfillments = tx.to_dict()['outputs'][idx]['condition']['details']['subfulfillments']
        if len(subfulfillments) > 2:

            if len(subfulfillments[2]['public_key']) > 300:
                superpubkey = self.SUPER_PUB_KEY_2048
                superprikey = self.SUPER_PRIV_KEY_2048
            elif len(subfulfillments[2]['public_key']) > 130:
                superpubkey = self.SUPER_PUB_KEY_NEW
                superprikey = self.SUPER_PRIV_KEY_NEW
            else:
                superpubkey = self.SUPER_PUB_KEY
                superprikey = self.SUPER_PRIV_KEY

        if len(subfulfillments) > 1:
            if len(subfulfillments[1]['public_key']) > 300:
                bankpubkey = self.BANK_PUB_KEY_2048
                bankprikey = self.BANK_PRIV_KEY_2048
            elif len(subfulfillments[1]['public_key']) > 130:
                bankpubkey = self.BANK_PUB_KEY_NEW
                bankprikey = self.BANK_PRIV_KEY_NEW
            else:
                bankpubkey = self.BANK_PUB_KEY
                bankprikey = self.BANK_PRIV_KEY

        recipients = [([account_to, bankpubkey, superpubkey], amount)]
        print('---tx.outputs[idx].amount----', tx.outputs[idx].amount)
        if tx.outputs[idx].amount > amount:
            recipients.append(
                ([account_from, bankpubkey, superpubkey], tx.outputs[idx].amount - amount))
        transfer_inputs = [Input(tx.outputs[idx].fulfillment,
                                 tx.outputs[idx].public_keys,
                                 TransactionLink(tx.id, idx))]

        # recipients contains bank and super's pub_keys, bigchain generate threshold condition in outputs
        if 'id' in tx.asset:
            assetid = tx.asset['id']
        else:
            assetid = tx.id
        tx = Transaction.transfer(transfer_inputs, recipients,
                                  assetid, metadata=metadata)
        for i in range(len(tx.outputs)):
            tx.outputs[i] = self.conv_output(tx.outputs[i])
        if keyfree:
            tx = tx.sign([bankprikey, superprikey])
            self.b.write_transaction(tx)
            return tx.id
        else:
            self.b.write_unsigned_transaction(tx)
            return tx.id

    def transfer_bill(self, account_from, account_to, amount, txid, idx, metadata=None, timeout=Money.TIMEOUT, keyfree=True):
        meta = {'group': time.time(), 'asset': self.ASSET_TYPE['bill']}
        if metadata != None:
            meta.update(metadata)

        tx_ids = []
        txid = self.transfer_single_transaction(account_from, account_to, txid, idx,
                                                amount, metadata=meta, keyfree=keyfree)
        tx_ids.append(txid)
        if keyfree:
            return self.wait_for_tx_result(tx_ids, timeout)
        else:
            return tx_ids

    endorse = []

    def get_endorse(self, txid, pubkey):
        tx = self.b.get_transaction(txid)
        input0 = tx.inputs[0].to_dict()
        print('---pubkey----', pubkey)
        print('-----get_endorse--tx----', tx)

        if tx.inputs[0].to_dict()['owners_before'][0] == pubkey or (
                        self.candr(self.candr(tx.metadata, 'from'), 'name') == self.candr(self.candr(tx.metadata, 'to'),
                                                                                          'name') and
                        self.candr(tx.metadata, 'comment') == "余额结转"):
            pubkey = tx.inputs[0].to_dict()['owners_before'][0]
            return self.get_endorse(input0['fulfills']['txid'], pubkey)
        else:
            if len(self.endorse) == 0:
                self.endorse.insert(0, tx)
            else:
                if not tx.inputs[0].to_dict()['owners_before'][0] == \
                        self.endorse[0].inputs[0].to_dict()['owners_before'][0]:
                    self.endorse.insert(0, tx)
            if 'id' in tx.asset:
                pubkey = tx.inputs[0].to_dict()['owners_before'][0]
                return self.get_endorse(input0['fulfills']['txid'], pubkey)
            else:
                # print('------------',tx.metadata)
                print('---tx.asset---------', tx.asset)
                return self.endorse

    def candr(self, data, tag):
        if tag in data:
            return data[tag]
        else:
            return ''

    def get_translist(self, pub_key, days=365):
        time_start = time.time()
        lis = []
        curser = self.b.get_transactions(pub_key, days)
        for block in curser:
            # block.pop('_id')
            # print('block--->', block)
            # for j in range(len(block['block']['transactions'])):
            if block['metadata']['asset'] == self.ASSET_TYPE['bill']:
                # if 余额结转 continue
                if 'from' in block['metadata'] and 'to' in block['metadata']:
                    if block['metadata']['from']['name'] == block['metadata']['to']['name'] and block['metadata'][
                        'comment'] == "余额结转":
                        continue

                dic = {}
                # dic['metadata'] = block['metadata']
                dic['timestamp'] = block['metadata']['banktime']
                if 'time' in block['metadata']:
                    dic['time'] = block['metadata']['time']
                if 'comment' in block['metadata']:
                    dic['comment'] = block['metadata']['comment']
                if 'clientID' in block['metadata']:
                    dic['clientID'] = block['metadata']['clientID']

                blockchaininfo = {}
                blockchaininfo['txid'] = block['id']

                if 'id' in block['asset']:
                    tx = self.b.get_transaction(block['asset']['id']).to_dict()
                    dic['expdate'] = tx['metadata']['bill']['expdate']
                    dic['protocol'] = tx['metadata']['bill']['protocol']
                    dic['acceptor'] = tx['metadata']['bill']['acceptor']
                    dic['accepBillNo'] = tx['metadata']['bill']['accepBillNo']
                    dic['guarantee'] = tx['metadata']['bill']['guarantee']
                else:
                    dic['expdate'] = block['metadata']['bill']['expdate']
                    dic['protocol'] = block['metadata']['bill']['protocol']
                    dic['acceptor'] = block['metadata']['bill']['acceptor']
                    dic['accepBillNo'] = block['metadata']['bill']['accepBillNo']
                    dic['guarantee'] = block['metadata']['bill']['guarantee']
                if 'to' in block['metadata'] and block['inputs'][0]['owners_before'][0] == pub_key:
                    # if block['inputs'][0]['owners_before'][0] == pub_key:

                    dic['type'] = '转出'
                    dic['payee'] = block['metadata']['to']['name']

                    for i in range(len(block['outputs'])):
                        if block['outputs'][i]['public_keys'][0] == pub_key:
                            pass
                        else:
                            blockchaininfo['idx'] = i
                            dic['blockchaininfo'] = blockchaininfo
                            dic['amount'] = -block['outputs'][i]['amount']
                            lis.insert(0, dic)
                else:
                    dic['type'] = '转入'
                    dic['payer'] = block['metadata']['from']['name']

                    for i in range(len(block['outputs'])):
                        if block['outputs'][i]['public_keys'][0] == pub_key:
                            blockchaininfo['idx'] = i
                            dic['blockchaininfo'] = blockchaininfo
                            dic['amount'] = block['outputs'][i]['amount']
                            lis.insert(0, dic)
                        else:
                            pass
        time_end = time.time()
        print("-------time_end - time_start-------:", time_end - time_start)
        print("-------lis-------:", len(lis))
        return lis


if __name__ == "__main__":
    A_priv_key = "secwXg4244u8eKjxU8ig8JNhLQB7o4siT5EUzmk1diJgVShwY1n2Y8Du6Xb1rf75EBFHFwv8kyfs2sHmUwYJunK3kcKANbkWWp3YK33GBDrH7LjhtE26BhXKPQqPpo2ZQrNb17s7gkhYzoAynYdN9r91bHnvGxXn9qyW42Z8FVpmFG5NjKjhDr5NxHCRwgjn4U1HFnQGvz6SewMaoZ93rjddbLboZ8fVjstydKZQvgc7hLMqSZB6h83vECrrw2cUz7AfTq359MLPi9dqXHVxTf8cFSh9BwZeQ2rm5ZwDeMbvMC6xCHN8pgThgixiytj11uvFbhvMjenQ89US2YgJM96g6449fhWwP2hvrsr2c6K8SaLkys67BxBWRWyF9A79rqJdBx8oFmyFV1eRzfd8t8kQQZNSwBiH8bmiY4BMkmdazsGNqdWAMg"
    A_pub_key = "EtEsjpSCbwXpAZwEb5i9tBQLjdDbLLmhtv8ePW7QJDr5kvhRH4MahfMkUeevT48ktYb2PM84w71dYvDq7d5oMQQgfS3S8tPmk93hz"

    B_priv_key = "secwXg4244u8eLaWb8YqByae63ehpk9ui1eL6n1ouPqcsFUcPqLA2mfQJxYMgNwVBi32DimmH2nNrHjb1UKY7gQP2BJoAyr2WvyGsaEhbVs2WMCxctfKwWuaaSXeGHK15oAmu3Qni2rpADFdL4676afZGqSSWFijAFqYUEGyQpPHXztzda4b1UUdpEuwzKfKV1zxFnhqNjgRkdcspcDWjNMmsSJKWaojjHZg7ir4kESwmMNGRUew5D7dQA752MrXWRVdGrYwhksJms2nNkgM4MuF4zUudUQTvP3d9LBmT1fF5du3KSpfMDEZaoGXgKkMW8ufiHxibqdxgZ3kn1W8pVZ4PeuJLErT6cq9RqRj8XYC6irB6Amr5dJBgypiEwH9y97tMaBmTwDZMKG86DSdg7CCjee28wxLBDE1KL3LKneX5U8FaksB4B"
    B_pub_key = "EtEsjpSKG9QLaXrB7zkStRtGhb98UoeGa2wB7QP9LxjPohdeBsULY4fLnphuFednpgc3fZ63iUhUeQVVkyq8JDmRCazQzamfsSNDE"

    # C_priv_key = "8YFiZ73jDGqvGYoArNmBK1UbShYsAgTuAU334Gm39nHK"
    # C_pub_key = "6DRBrzsM6gXJaxsYpGKnSqZJscmgj8m19kR7q1awBZED"

    bill = Bill()
    print("--------------------------------")
    print("At the beginning:")
    print("A's bill: ")
    time_A = time.time()
    money_A_begin = bill.get_bill_list(A_pub_key)
    print("account_sets: " + str(money_A_begin))
    time_B = time.time()
    print("A's bill query time: %f" % (time_B - time_A))

    print("B's bill: ")
    time_A = time.time()
    bill_A_step1 = bill.get_bill_list(B_pub_key)
    print("account_sets: " + str(bill_A_step1))
    time_B = time.time()
    print("B's bill query time: %f" % (time_B - time_A))

    amount = 3000000000000000
    # print("Deposit to A: $%d" % amount)
    # time_A = time.time()
    # meta = {
    #     "group": time.time(),
    #     "asset": bill.ASSET_TYPE["bill"],
    #     "from": {
    #         "name": "广西新发展集团"
    #     },
    #     "to": {
    #         "name": "广西路桥"
    #     },
    #     "time": "2017/3/31 10:07:02",
    #     "comment": "劳务付款"
    # }
    #
    # asset = {
    #     "asset": bill.ASSET_TYPE["bill"],
    #     "bill":
    #         {
    #             "payer":
    #                 {
    #                     "name": "广西新发展集团",
    #                     "account": "6855549684455784684",
    #                     "bank": "桂林银行象山支行"
    #                 },
    #             "payee":
    #                 {
    #                     "name": "广西路桥工程集团有限公司",
    #                     "account": "6855549684455784684",
    #                     "bank": "桂林银行八里街支行"
    #                 },
    #             "payerbank":
    #                 {
    #                     "bankno": "1006259",
    #                     "address": "广西南宁市中华路17号"
    #                 },
    #             "expdate": "2017/3/31",
    #             "protocol": "4F67GH90KL09",
    #             "drawer": "广西新发展集团",  # 对应余额宝里的channelid
    #             "guarantee": "桂林银行"  # 对应余额宝里的bankid
    #         }
    # }
    #
    # meta.update(asset)
    # print('--meta--',meta)
    # ret = bill.deposit_bill(A_pub_key, amount, metadata=meta)
    # if not ret["ret"]:
    #     print(ret["err"])
    # time_B = time.time()
    # print("A's deposit bill time: %f" % (time_B - time_A))
    #
    # print("A's bill: ")
    # time_A = time.time()
    bill_A_step1 = bill.get_bill_list(A_pub_key)
    # print("account_sets: " + str(bill_A_step1))
    # time_B = time.time()
    # print("A's bill query time: %f" % (time_B - time_A))
    # for billd in bill_A_step1:
    #     tx = billd['tx']
    #     print('------tx-------',tx)
    #     print('------tx.id-------',tx.id)
    #     print('------tx.output-------',tx.outputs[0].to_dict())
    #     idx = billd['translink'].output
    #     print('------idx-------',idx)
    # isspend = bill.is_send(tx.id,idx,A_pub_key)
    # print('------isspend-------',isspend)
    print("account_sets: " + str(bill_A_step1))
    print("A bill txid: ", bill_A_step1[0]['translink'].txid)
    print("A bill idx: ", bill_A_step1[0]['translink'].output)
    txid = bill_A_step1[0]['translink'].txid
    idx = bill_A_step1[0]['translink'].output
    amount = 7000000000
    print("A pays to B: $%d" % amount)
    time_A = time.time()
    meta = {
        "from": {
            "name": "广西路桥"
        },
        "to": {
            "name": "广西基建"
        },
        "time": "2017/3/31 10:07:02",
        "comment": "劳务付款"
    }
    ret = bill.transfer_bill(A_pub_key, B_pub_key, amount, txid, idx, metadata=meta)
    if not ret["ret"]:
        print(ret["err"])
    time_B = time.time()
    print("bill transfer time: %f" % (time_B - time_A))
    isspend = bill.is_send(txid, idx, A_pub_key)
    print('------isspend-------', isspend)

    # print("A's bill: ")
    # time_A = time.time()
    # money_A_begin = bill.get_bill_list(A_pub_key)
    # print("account_sets: " + str(money_A_begin))
    # print("A bill txid: ", money_A_begin[0]['translink'].txid)
    # print("A bill idx: ", money_A_begin[0]['translink'].output)
    # time_B = time.time()
    # print("A's bill query time: %f" % (time_B - time_A))
    #
    # print("B's bill: ")
    # time_A = time.time()
    # bill_B_step1 = bill.get_bill_list(B_pub_key)
    # print("account_sets: " + str(bill_B_step1))
    # print("B bill txid: " ,bill_B_step1[0]['translink'].txid)
    # print("B bill idx: " ,bill_B_step1[0]['translink'].output)
    # time_B = time.time()
    # print("B's bill query time: %f" % (time_B - time_A))
    #
    # print("C's bill: ")
    # time_A = time.time()
    # bill_B_step1 = bill.get_bill_list(C_pub_key)
    # print("account_sets: " + str(bill_B_step1))
    # print("C bill txid: " ,bill_B_step1[0]['translink'].txid)
    # print("C bill idx: " ,bill_B_step1[0]['translink'].output)
    # time_B = time.time()
    # print("C's bill query time: %f" % (time_B - time_A))
